Issue #015
March, 1997


Contents:

Java Test Suites
JDK 1.1
Introduction to Java Security Part 2 - Verification
Books on Java
Comparing C/C++ and Java Part 15 - Bitfields
Introduction to AWT Programming Part 2 - Another Example


JAVA TEST SUITES

I am currently working on developing commercial test suites for
several of the Java API packages (libraries).  The first two of these
will cover reflection (java.lang.reflect) and utilities (java.util).

These suites exhaustively test the features of the API.  For example,
the one on reflection is about 6000 lines of code.

If you have an interest in obtaining these products, please contact me
(glenm@glenmccl.com).


JDK 1.1

Javasoft has released the official version of the Java Development Kit
1.1.  You can download it at:

        http://java.sun.com/products/JDK/1.1

JDK 1.1 contains many new features, such as reflection and
serialization.


INTRODUCTION TO JAVA SECURITY PART 2 - VERIFICATION

We saw last time how the Java virtual machine model insulates a Java
program from hardware, and how this is both desirable and undesirable.

In this issue we'll talk a bit about verification, another aspect of
security.  A java class is compiled into platform-independent ".class"
files containing byte streams.  These streams contain the actual
program codes (bytecodes, constants, debugging information, and so on).

Suppose that I have the hello program:

        public class hello {

                public static void main(String args[])
                {
                        System.out.println("Hello World");
                }

        }

and I compile it:

        $ javac hello.java

resulting in a "hello.class" file of 461 bytes (in JDK 1.1).  And I'm
feeling malicious and decide to tweak one of the bytes:

        import java.io.*;

        public class tweak {

                public static void main(String args[])
                {
                        int offset = Integer.parseInt(args[0]);
                        try {
                                RandomAccessFile raf =
                                    new RandomAccessFile("hello.class", "rw");
                                raf.seek(offset);
                                raf.writeByte(97);
                                raf.close();
                        }
                        catch (Throwable e) {
                                System.err.println("*** exception ***");
                        }
                }

        }

by saying:

        $ javac tweak.java

        $ java tweak 0

thereby writing the byte "97" into location 0 in "hello.class".
Obviously, this is not how Java was intended to be used.  What will
happen?

It turns out that the first four bytes of a Java .class file must be
the hex value "0xCAFEBABE", and writing 0 into one of these will
invalidate the file.  If I then try to run the program:

        $ java hello

I will get an immediate error.  This particular feature is fairly
common in program binaries and often goes by the name of "magic
number".

When a Java program is run, the interpreter first invokes the Java
Verifier.  The Verifier checks the magic number along with other
properties of the .class file, including:

        - stack overflows

        - no illegal data conversions

        - access to public/private/protected

        - proper format for the .class file

        - methods have appropriate arguments

There are many other checks done by the Verifier on .class files.
This list is merely illustrative.  Verifying a class not only improves
security, but speeds up actual bytecode interpretation because the
checks don't have to be repeated.

An interesting book that goes into detail on Java security is "Java
Security - Hostile Applets, Holes, and Antidotes", by Gary McGraw and
Edward Felten, published 1997 by Wiley for $20.

We will be discussing security further in future issues.


BOOKS ON JAVA

If you have Web access, there is a page that has a comprehensive list
of Java books that have been published or are in the works:

        http://lightyear.ncsa.uiuc.edu/~srp/java/javabooks.html

Several hundred books are listed.


COMPARING C/C++ AND JAVA PART 15 - BITFIELDS

In C and C++, it's possible to have usage such as:

        struct A {
                int x : 5;
                int y : 2;
                int z : 1;
        };

with the idea being that the data fields take the indicated number of
bits.  In theory this particular structure would require 8 bits or one
byte per instance.  In practice, because of padding and packing
issues, each instance might take more than a byte.

Bitfields are sometimes used for interfacing to hardware, for example
where specific bits in a machine register have particular meaning.

Java has no bitfields.  As we discussed in the previous issue of the
newsletter, the language is pitched at a somewhat higher level than C
and C++, and there's no direct way to manipulate hardware resources.

And Java has a variety of types, such as boolean and byte, that
support efficient representations of data.


INTRODUCTION TO AWT PROGRAMMING PART 2 - ANOTHER EXAMPLE

In the last issue we started a series on AWT (Abstract Window Toolkit)
programming.  We presented an example of writing a "more" program, one
that can be used to page through text from a file.  In this issue
we'll show another more complicated and powerful way of achieving the
same end.

Before diving into this, a few general comments are in order.  This
code is written against JDK 1.1.  It gives some "deprecation"
warnings, about methods which have changed in the JDK.  As such, the
code needs to be updated, but doing so would make it not work with
earlier versions.  This is simply something that we have to put up
with in an evolving language.

Also, this code reveals a bug in the JDK 1.1 implementation for
Windows NT.  If you compile this program and run it, you will notice
that scroll bars are flaky.  This is a known bug that is supposed to
be fixed in a bug fix release of 1.1.

Finally, we are getting into an area that is perhaps not as stable as
some other parts of Java, and still evolving (as is my understanding
of it).  Some aspects of the example below, notably layout manager
usage, are a bit tricky.

As you will recall, the previous version of the "more" program had one
big deficiency, namely, that it read in a whole file before displaying
it.  This is not acceptable for a very large file, especially with
relatively slow I/O.  We've fixed that problem, and to do so, set up
our own text windowing scheme with scroll bars which we manage.

Here is the actual code.  If you scan down the left side, you can see
interspersed comments starting with "//" in the left margin.

        import java.awt.*;
        import java.io.*;

        // a text area in the window
        
// This is the class used to manage a text area, with scroll bars.
// It is passed a RandomAccessFile object to read lines from, and
// reads them on demand only.

// A Canvas is an AWT object suitable for displaying text on.

        class Text extends Canvas {
        
                private static final int LISTSIZ = 16;
                private String list[] = null;
                private int scnt = 0;
                private int currpos = 0;
                private RandomAccessFile raf = null;
                private boolean ateof = false;
        
                // initialize
                void init(RandomAccessFile r)
                {
                        if (raf != null) {
                                try {
                                        raf.close();
                                }
                                catch (Throwable e) {
                                        System.err.println("close err");
                                        System.exit(1);
                                }
                        }
                        raf = r;
                        ateof = false;
                        currpos = 0;
        
                        scnt = 0;
                        list = new String[LISTSIZ];
                }
        
// We need to detab Strings that are displayed, because tabs
// are handled in a funny way by Graphics.drawString().

                // detab a String
                private static String detab(String s)
                {
                        StringBuffer sb = new StringBuffer();
                        int len = s.length();
                        int pos = 0;
                        for (int i = 0; i < len; i++) {
                                char c = s.charAt(i);
                                if (c == '\r' || c == '\n') {
                                }
                                else if (c == '\t') {
                                        do {
                                                sb.append(' ');
                                                pos++;
                                        } while (pos % 8 != 0);
                                }
                                else {
                                        sb.append(c);
                                        pos++;
                                }
                        }
                        return sb.toString();
                }
        
// This is the internal list used to store lines to be displayed.
// We could also use java.util.Vector to manage this list, and
// System.arraycopy() to copy the list.

                // add a String to the list
                void add(String s)
                {
                        if (scnt == list.length) {
                                String x[] = new String[list.length * 3 / 2];
                                for (int i = 0; i < scnt; i++)
                                        x[i] = list[i];
                                list = x;
                        }
                        list[scnt++] = detab(s);
                }
        
// Called by the handleEvent() method below.

                // scroll up or down a line
                void scroll(int dir)
                {
                        currpos += dir;
        
                        if (currpos < 0)
                                currpos = 0;
        
                        repaint();
                }
        
// This is the paint() method, used to draw lines in the text window.
// We use FontMetrics to determine how tall a character is, and display
// as many lines as will fit.

                // paint
                public void paint(Graphics g)
                {
                        Dimension d = size();
                        int startpos = More.HEIGHT - d.height;
                        int pos = startpos;
                        int sp = g.getFontMetrics().getHeight();
                        int i = currpos;
        
                        while (pos + sp < d.height) {
                                while (i >= scnt && !ateof) {
                                        String s = null;
                                        try {
                                                s = raf.readLine();
                                        }
                                        catch (Throwable e) {
                                                System.err.println("I/O err");
                                                System.exit(1);
                                        }
                                        if (s == null)
                                                ateof = true;
                                        else
                                                add(s);
                                }
                                if (i >= scnt)
                                        break;
                                g.drawString(list[i], 5, pos);
                                i++;
                                pos += sp;
                        }
                }
        
        }
        
// We use a Panel to contain the text area, along with scroll bars.
// A Panel is an AWT object that can contain other objects.

        // a Panel for the text area, with scroll bars
        
        class Panel_ta extends Panel {
        
                private Scrollbar vbar = null;
                private Text t = null;
        
                // constructor
                Panel_ta(Text ta)
                {
                        vbar = new Scrollbar(Scrollbar.VERTICAL);

// Put the text area in the center, and the scroll bar on the
// "East" side.
                        setLayout(new BorderLayout(0, 0));
                        add("Center", ta);
                        add("East", vbar);
                        t = ta;
                }
        
// handleEvent() is called for actual scrolling.

                // handle scrolling
                public boolean handleEvent(Event e)
                {
                        if (e.target == vbar) {
                                switch (e.id) {
                                        case Event.SCROLL_LINE_UP:
                                                t.scroll(-1);
                                                break;
                                        case Event.SCROLL_LINE_DOWN:
                                                t.scroll(1);
                                                break;
                                }
                                return true;
                        }
                        else {
                                return super.handleEvent(e);
                        }
                }
        
        }
        
// The actual "More" class.

        public class More extends Frame {
        
                private Button b1 = new Button("New File");
                private Button b2 = new Button("Exit");
                private Text ta = new Text();
        
                static final int WIDTH = 600;
                static final int HEIGHT = 450;
        
// A dialog invoked when the user selects "New File".

                // open a new file
                private void new_file()
                {
                        FileDialog fd = new FileDialog(this, "Open File",
                            FileDialog.LOAD);
                        fd.show();
                        if (fd.getDirectory() == null ||
                            fd.getDirectory().equals(""))
                                return;
                        if (fd.getFile() == null || fd.getFile().equals(""))
                                return;
                        if (fd.getFile().equals("*.*"))
                                return;
                        String fn = fd.getDirectory() + File.separator +
                            fd.getFile();
                        load(fn);
                }
        
// Called when buttons are selected.

                // handle an action
                public boolean action(Event e, Object arg)
                {
                        if (e.target instanceof Button) {
                                if (e.target == b1) {
                                        new_file();
                                        return true;
                                }
                                if (e.target == b2) {
                                        dispose();
                                        System.exit(0);
                                        return true;
                                }
                                return false;
                        }
                        else {
                                return false;
                        } 
                }
        
// Load a new file in.

                // load a file
                private void load(String fn)
                {
                        try {
                                RandomAccessFile raf =
                                    new RandomAccessFile(fn, "r");
                                ta.init(raf);
                        }
                        catch (Throwable e) {
                                System.err.println("open err");
                                System.exit(1);
                        }
        
                        ta.repaint();
                }
        
// Constructor for More.

                // constructor
                public More(String title, String fn)
                {
                        super(title);
        
                        resize(WIDTH, HEIGHT);
        
// Set a layout manager for the buttons, flowing across the screen.

                        Panel p1 = new Panel();
                        p1.setLayout(new FlowLayout(FlowLayout.CENTER,50,0));
        
                        p1.add(b1);
                        p1.add(b2);
        
// Set a fixed-width font, size 12.

                        ta.setFont(new Font("Courier", Font.PLAIN, 12));
        
                        Panel p2 = new Panel_ta(ta);
        
// Set a layout manager for the buttons at the top, and the text area
// at the bottom.  GridBagLayout is the most complicated and powerful
// of the layout managers.  You can specify cell positions, cell
// weights, and so on.  Each object to be laid out has its constraints
// set for it.

// In this case, we give the top row of buttons a weight of 1, and the
// text area a weight of 7, so the text area will take 7/8 of the total
// window.

                        GridBagLayout gbl = new GridBagLayout();
        
                        GridBagConstraints gbc = new GridBagConstraints();
        
                        setLayout(gbl);
        
                        gbc.gridx = 0;
                        gbc.gridy = 0;
                        gbc.weightx = 1;
                        gbc.weighty = 1;
                        gbc.fill = GridBagConstraints.BOTH;
                        gbc.anchor = GridBagConstraints.CENTER;
        
                        gbl.setConstraints(p1, gbc);
        
                        gbc.gridy = 1;
                        gbc.weighty = 7;
        
                        gbl.setConstraints(p2, gbc);
        
                        add(p1);
                        add(p2);
        
                        load(fn);

// Actually display the window.
        
                        show();
                }
        
// Driver main() method.

                // driver
                public static void main(String args[])
                {
                        Frame f = new More("More", args[0]);
                }
        
        }

This example is fairly complicated, but illustrates several important
points about the AWT.  There are other ways to approach this problem.
Another feature we could add would be one to search through the file
for a specified string, and display the line it's found on.


ACKNOWLEDGEMENTS
 
Thanks to Jay Burgess, Thierry Ciot, Irv Kanode, Mike Paluka, Anand
Ramachandran, Srihari Sampathkumar, Jason Sharp, and Bob Shore for
help with proofreading.


SUBSCRIPTION INFORMATION / BACK ISSUES

To subscribe to the newsletter, send mail to majordomo@world.std.com
with this line as its message body:

subscribe java_letter

Back issues are available via FTP from:

        rmi.net /pub2/glenm/javalett

or on the Web at:

        http://rainbow.rmi.net/~glenm

There is also a C++ newsletter.  To subscribe to it, say:

subscribe c_plus_plus

using the same majordomo@world.std.com address.

-------------------------

Copyright (c) 1997 Glen McCluskey.  All Rights Reserved.

This newsletter may be further distributed provided that it is copied
in its entirety, including the newsletter number at the top and the
copyright and contact information at the bottom.

Glen McCluskey & Associates
Professional Computer Consulting
Internet: glenm@glenmccl.com
Phone: (800) 722-1613 or (970) 490-2462
Fax: (970) 490-2463
FTP: rmi.net /pub2/glenm/javalett (for back issues)
Web: http://rainbow.rmi.net/~glenm
